import { Spin } from 'antd'
import { useEffect, useRef, useState } from 'react'
import { useSnapshot } from 'valtio'
import { LoadingOutlined } from '@ant-design/icons'
import srcDocText from './srcdoc.html?raw'
import schemaTransform from './schemaTransform'
import SelectComponent from './SelectComponent'
import { PreviewProxy } from './PreviewProxy'
import {
  currentState,
  dependencyConfigState,
  jsonSchema,
  schemaMapState,
} from '@/store'
import { createId } from '@/utils'
import type { JSONSchema } from '@/store/Schema'

const sandboxAttr = [
  'allow-forms',
  'allow-modals',
  'allow-pointer-lock',
  'allow-popups',
  'allow-same-origin',
  'allow-scripts',
  'allow-top-navigation-by-user-activation',
].join(' ')

function transformComponent(props: JSONSchema['componentsTree']) {
  for (const key in props) {
    const item = props[key]
    if (item && item.$componentName && !item?.componentid)
      item.componentid = createId()
    else if (item && typeof item === 'object')
      transformComponent(props[key] as any)
  }
}

function Simulator(props: {
  mode: 'preview' | 'code'
}) {
  const { mode } = props
  const depsMapSnap = useSnapshot(dependencyConfigState).dependencyConfigState
  const iframeRef = useRef<HTMLIFrameElement>(null)
  const iframeDocRef = useRef<Document | null>(null)
  const proxyRef = useRef<PreviewProxy>()
  const [srcdocState, setSrcdocState] = useState(srcDocText)
  const jsonSchemaSnap = useSnapshot(jsonSchema).value
  const schemaMapSnap = useSnapshot(schemaMapState).schemaMap
  const [frameLoading, setFrameLoading] = useState(false)
  const wrapper = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (
      !iframeDocRef.current
      && iframeRef.current?.contentWindow?.document
      && iframeRef.current?.contentWindow?.document
    ) {
      const resize = () => {
        if (!iframeRef.current) {
          console.warn('iframeRef.current不存在')
          return
        }
        iframeDocRef.current = iframeRef.current?.contentWindow?.document as any
        iframeRef.current!.style.width = `${wrapper.current?.clientWidth}px`
        iframeRef.current!.style.height = `${wrapper.current?.clientHeight}px`
      }
      const obs = new ResizeObserver(resize)
      if (wrapper.current?.parentNode)
        obs.observe(wrapper.current?.parentNode as any, {})

      resize()
      window.addEventListener('resize', resize)
      return () => {
        window.removeEventListener('resize', resize)
      }
    }
  }, [iframeDocRef.current])

  useEffect(() => {
    const scriptText = depsMapSnap
      .map((dependency) => {
        return `<script data-sandbox-script=${dependency.libraryName}  src="${dependency.libraryUrl}"> </script>`
      })
      .join('\r')

    const linkText = depsMapSnap
      .map((dependency) => {
        return dependency.linkUrl
          ?.map(href => ` <link rel="stylesheet" href="${href}"></link>`)
          .join('\n')
      })
      .join('\n')

    const str = srcdocState
      .replace('<!-- scripts -->', scriptText)
      .replace('<!-- links -->', linkText)
    setFrameLoading(true)
    setSrcdocState(str)
  }, [depsMapSnap])

  useEffect(() => {
    if (frameLoading)
      return
    transformComponent(jsonSchema.value.componentsTree)
    schemaTransform(JSON.parse(JSON.stringify(jsonSchemaSnap))).then(
      (jsxCode) => {
        const scriptTag = document.createElement('script')
        scriptTag.id = 'render-jsx'
        scriptTag.setAttribute('type', 'text/babel')
        if (
          scriptTag
          && iframeRef.current?.contentDocument?.body?.appendChild
        ) {
          proxyRef.current?.iframe_command('render', {
            code: jsxCode,
          })
          currentState.code = jsxCode[0] || '' // 用于展示
        }
      },
    )
  }, [schemaMapSnap, frameLoading, jsonSchemaSnap])

  useEffect(() => {
    if (iframeRef.current) {
      proxyRef.current = new PreviewProxy(iframeRef.current, {})
      iframeRef.current!.addEventListener('load', () => {
        setFrameLoading(false)
      })
      iframeRef.current!.addEventListener('error', () => {
        setFrameLoading(false)
      })
    }
  }, [])

  return (
    <div className={`border-none flex-1 flex bg-#fff absolute ${mode === 'code' ? 'top-40px left-2% right-2% bottom-2%' : 'top-1%  left-1% right-1% bottom-1%'}   shadow-2xl`} ref={wrapper}>
      <Spin
        indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
        spinning={frameLoading}
        tip={
          `正在加载资源 ${depsMapSnap.map(deps => deps.libraryName).join('、')
          } 请稍后 ...`
        }
      >
        <iframe
          id="sanbox-simulator"
          className="border-none relative "
          srcDoc={srcdocState}
          sandbox={sandboxAttr}
          ref={iframeRef}
        ></iframe>
      </Spin>
      {!frameLoading && mode === 'code' && <SelectComponent />}
    </div>
  )
}

export default Simulator
